package net.butov.wrappers.wrapper;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;

/**
 * @author Maxim Butov
 * @version $Id$
 */
public class Helper {

    public static <S, W> W createWrapper(final S source, final Class<W> wrapperClass) {

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(wrapperClass);

        Set<Class> allInterfaces = new HashSet<Class>();
        allInterfaces.addAll(Arrays.asList(source.getClass().getInterfaces()));
        allInterfaces.addAll(Arrays.asList(wrapperClass.getInterfaces()));
        enhancer.setInterfaces(allInterfaces.toArray(new Class[allInterfaces.size()]));
        
        enhancer.setCallback(new MethodInterceptor() {

            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                if ("getSource".equals(method.getName()) && Wrapper.class.equals(method.getDeclaringClass())) {
                    return source;
                }

                if (Arrays.asList(wrapperClass.getDeclaredMethods()).contains(method)) {
                    return methodProxy.invokeSuper(proxy, args);
                }

                return methodProxy.invoke(source, args);
            }
        });

        return (W) enhancer.create();
    }
}
